//---------------------------------------------------------------------------------
// Copyright (c) 2011, Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//---------------------------------------------------------------------------------

namespace MessageBufferClient
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Net;
    using System.Net.Browser;
    using System.Text;
    using System.Threading;
    using System.Windows;
    using System.Windows.Browser;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;

    public partial class MainPage : UserControl
    {
        const string AccessControlUriPostFix = "accesscontrol.windows.net";
        const string ServiceBusUriPostFix = "servicebus.windows.net";

        const string DefaultPolicy =
            "<entry xmlns=\"http://www.w3.org/2005/Atom\">" +
              "<content type=\"text/xml\">" +
                "<MessageBufferPolicy xmlns=\"http://schemas.microsoft.com/netservices/2009/05/servicebus/connect\"/>" +
               "</content>" +
            "</entry>";

        const string XMSAuthorizationHeader = "X-MS-Authorization";

        string messageBufferScopeUri;
        string messageBufferLocation;
        string authHeaderValue;
        SynchronizationContext currentContext;
        
        public MainPage()
        {
            InitializeComponent();

            this.currentContext = SynchronizationContext.Current;

            if (!WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp))
            {
                this.currentContext.Post(new SendOrPostCallback(this.DisplayCallback), new DisplayData("MainForm", "Failed to register the HTTPS prefix with the Silverlight's client stack", true));
            }

            if (!WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp))
            {
                this.currentContext.Post(new SendOrPostCallback(this.DisplayCallback), new DisplayData("MainForm", "Failed to register the HTTP prefix with the Silverlight's client stack", true));
            }
        }

        public delegate void MessageBufferUploadOperationHandler(UploadStringCompletedEventHandler messagBufferUploadOpCompleted);

        public delegate void MessageBufferDownloadOperationHandler(DownloadStringCompletedEventHandler messagBufferDownloadOpCompleted);

        public void BtnCreateMessageBuffer_Click(object sender, RoutedEventArgs e)
        {
            this.tbMessage.Text = string.Empty;

            if (this.CheckForRequiredFieldsAndUpdateUris())
            {
                this.GetToken(new MessageBufferUploadOperationHandler(this.CreateMessageBuffer));
            }
        }

        public void BtnSendMessage_Click(object sender, RoutedEventArgs e)
        {
            this.tbMessage.Text = string.Empty;

            if (this.CheckForRequiredFieldsAndUpdateUris())
            {
                this.GetToken(new MessageBufferUploadOperationHandler(this.SendMessage));
            }            
        }

        public void BtnRetrieveMessage_Click(object sender, RoutedEventArgs e)
        {
            this.tbMessage.Text = string.Empty;

            if (this.CheckForRequiredFieldsAndUpdateUris())
            {
                this.GetToken(new MessageBufferUploadOperationHandler(this.RetrieveMessage));
            }
        }

        public void BtnGetMessageBuffer_Click(object sender, RoutedEventArgs e)
        {
            this.tbMessage.Text = string.Empty;

            if (this.CheckForRequiredFieldsAndUpdateUris())
            {
                this.GetToken(new MessageBufferDownloadOperationHandler(this.GetMessageBuffer));
            }
        }

        public void BtnDeleteMessageBuffer_Click(object sender, RoutedEventArgs e)
        {
            this.tbMessage.Text = string.Empty;

            if (this.CheckForRequiredFieldsAndUpdateUris())
            {
                this.GetToken(new MessageBufferUploadOperationHandler(this.DeleteMessageBuffer));
            }
        }        

        public void GetToken(object messageBufferOperationHandler)
        {
            string acsUri = string.Format("https://{0}-sb.{1}/WRAPv0.9/", this.serviceNamespaceTextBox.Text, AccessControlUriPostFix);
            string messageBufferLocation = string.Empty;
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format("wrap_name={0}", HttpUtility.UrlEncode(this.issuerNameTextBox.Text)));
            sb.Append(string.Format("&wrap_password={0}", HttpUtility.UrlEncode(this.issuerKeyTextBox.Text)));
            sb.Append(string.Format("&wrap_scope={0}", HttpUtility.UrlEncode(this.messageBufferScopeUri)));
            
            WebClient client = new WebClient();
            client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
            client.UploadStringCompleted += new UploadStringCompletedEventHandler(this.GetTokenCompleted);

            this.AppendOutputText("GetToken: Sending request to get token");

            client.UploadStringAsync(new Uri(acsUri), "POST", sb.ToString(), messageBufferOperationHandler);
        }        

        public void CreateMessageBuffer(UploadStringCompletedEventHandler createMessageBufferCompleted)
        {
            string bufferLocation = AddTrailingSlashIfNeeded(this.messageBufferLocation);

            WebClient webClient = new WebClient();
            webClient.BaseAddress = bufferLocation;
            webClient.Headers[XMSAuthorizationHeader] = this.authHeaderValue;
            webClient.Headers[HttpRequestHeader.ContentType] = "application/atom+xml;type=entry;charset=utf-8";
            webClient.UploadStringCompleted += createMessageBufferCompleted;

            this.AppendOutputText("MessageBuffer.Create: Sending request to create message buffer");
            webClient.UploadStringAsync(new Uri(bufferLocation), "PUT", DefaultPolicy, "MessageBuffer.Create");
        }

        public void SendMessage(UploadStringCompletedEventHandler sendMessageCompleted)
        {
            string message = string.Format("<msg>This is a test message, Timestamp: " + DateTime.Now + "</msg>");

            string bufferLocation = AddTrailingSlashIfNeeded(this.messageBufferLocation);
            WebClient webClient = new WebClient();
            webClient.BaseAddress = bufferLocation;
            webClient.Headers[XMSAuthorizationHeader] = this.authHeaderValue;
            webClient.Headers[HttpRequestHeader.ContentType] = "text/xml";
            webClient.UploadStringCompleted += sendMessageCompleted;
            bufferLocation += "messages?timeout=30";

            this.AppendOutputText("MessageBuffer.Send: Sending request to send message: " + message);
            webClient.UploadStringAsync(new Uri(bufferLocation), "POST", message, "MessageBuffer.Send");
        }

        public void RetrieveMessage(UploadStringCompletedEventHandler retrieveMessageCompleted)
        {
            string bufferLocation = AddTrailingSlashIfNeeded(this.messageBufferLocation);

            WebClient webClient = new WebClient();
            webClient.BaseAddress = bufferLocation;
            webClient.Headers[XMSAuthorizationHeader] = this.authHeaderValue;
            webClient.UploadStringCompleted += retrieveMessageCompleted;
            bufferLocation += "messages/head?timeout=30";

            this.AppendOutputText("MessageBuffer.Retrieve: Sending request to retrieve message");
            webClient.UploadStringAsync(new Uri(bufferLocation), "DELETE", string.Empty, "MessageBuffer.Retrieve");
        }

        public void GetMessageBuffer(DownloadStringCompletedEventHandler getMessageBufferCompleted)
        {
            string bufferLocation = AddTrailingSlashIfNeeded(this.messageBufferLocation);

            WebClient webClient = new WebClient();
            webClient.BaseAddress = bufferLocation;
            webClient.Headers[XMSAuthorizationHeader] = this.authHeaderValue;
            webClient.DownloadStringCompleted += getMessageBufferCompleted;

            this.AppendOutputText("MessageBuffer.Get: Sending request to get message buffer policy");
            webClient.DownloadStringAsync(new Uri(bufferLocation), "MessageBuffer.Get");
        }

        public void DeleteMessageBuffer(UploadStringCompletedEventHandler deleteMessageBufferCompleted)
        {
            string bufferLocation = AddTrailingSlashIfNeeded(this.messageBufferLocation);

            WebClient webClient = new WebClient();
            webClient.BaseAddress = bufferLocation;
            webClient.Headers[XMSAuthorizationHeader] = this.authHeaderValue;
            webClient.UploadStringCompleted += deleteMessageBufferCompleted;

            this.AppendOutputText("MessageBuffer.Delete: Sending request to delete message buffer");
            webClient.UploadStringAsync(new Uri(bufferLocation), "DELETE", string.Empty, "MessageBuffer.Delete");
        }

        private static string AddTrailingSlashIfNeeded(string messageBufferUri)
        {

            if (messageBufferUri[messageBufferUri.Length - 1] != '/')
            {
                messageBufferUri += '/';
            }

            return messageBufferUri;
        }

        private void AppendOutputText(string output)
        {
            this.tbMessage.Text += output + "\n";
        }        

        private bool CheckForRequiredFieldsAndUpdateUris()
        {
            if (string.IsNullOrEmpty(this.issuerNameTextBox.Text) ||
                string.IsNullOrEmpty(this.issuerKeyTextBox.Text) ||
                string.IsNullOrEmpty(this.serviceNamespaceTextBox.Text) ||
                string.IsNullOrEmpty(this.messageBufferNameTextBox.Text))
            {
                this.AppendOutputText("MainForm: Required fields missing, please fill missing info");
                return false;
            }
            else
            {               
                this.messageBufferScopeUri = string.Format("http://{0}.{1}/", this.serviceNamespaceTextBox.Text, ServiceBusUriPostFix);
                this.messageBufferLocation = string.Format("https://{0}.{1}/{2}", this.serviceNamespaceTextBox.Text, ServiceBusUriPostFix, this.messageBufferNameTextBox.Text);

                return true;
            }
        }

        private void GetTokenCompleted(object sender, UploadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                // A typical response body (with fields blanked for security): wrap_access_token=[claim type]%3d[claim value]%26Issuer%3d[service namespace]%26Audience%3d[audience scope]%26ExpiresOn%3d[token policy expires on]%26HMACSHA256%3d[signature]&wrap_access_token_expires_in=[some int]
                // Get the wrap_access_token only:
                string token = Uri.UnescapeDataString(e.Result.Split('&').Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase)).Split('=')[1]);
                this.authHeaderValue = String.Format("WRAP access_token=\"{0}\"", token);

                this.AppendOutputText("GetToken: Retrieved token successfully");
            }
            else
            {
                this.currentContext.Post(new SendOrPostCallback(this.DisplayCallback), new DisplayData("GetToken", e.Error, true));
                return;
            }

            if (e.UserState is MessageBufferUploadOperationHandler)
            {
                MessageBufferUploadOperationHandler messageBufferOpHandler = (MessageBufferUploadOperationHandler)e.UserState;
                messageBufferOpHandler(this.MessageBufferUploadOperationCompleted);
            }
            else if (e.UserState is MessageBufferDownloadOperationHandler)
            {
                MessageBufferDownloadOperationHandler messageBufferOpHandler = (MessageBufferDownloadOperationHandler)e.UserState;
                messageBufferOpHandler(this.MessageBufferDownloadOperationCompleted);
            }
        }

        private void MessageBufferUploadOperationCompleted(object webClient, UploadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                this.currentContext.Post(new SendOrPostCallback(this.DisplayCallback), new DisplayData((string)e.UserState, e.Result, false));
            }
            else
            {
                this.currentContext.Post(new SendOrPostCallback(this.DisplayCallback), new DisplayData((string)e.UserState, e.Error, true));
            }
        }

        private void MessageBufferDownloadOperationCompleted(object webClient, DownloadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                this.currentContext.Post(new SendOrPostCallback(this.DisplayCallback), new DisplayData((string)e.UserState, e.Result, false));
            }
            else
            {
                this.currentContext.Post(new SendOrPostCallback(this.DisplayCallback), new DisplayData((string)e.UserState, e.Error, true));
            }
        }

        private void DisplayCallback(object state)
        {
            DisplayData displayData = state as DisplayData;

            if (displayData.Error)
            {
                this.tbMessage.Foreground = new SolidColorBrush(Colors.Red);
            }
            else
            {
                this.tbMessage.Foreground = new SolidColorBrush(Colors.Black);
            }

            if (!string.IsNullOrEmpty(displayData.Payload.ToString()))
            {
                this.AppendOutputText(displayData.Operation + ": " + displayData.Payload.ToString());
            }
            else
            {
                if (displayData.Error)
                {
                    this.AppendOutputText(displayData.Operation + ": " + "Message Buffer Operation failed");
                }
                else
                {
                    this.AppendOutputText(displayData.Operation + ": " + "Message Buffer Operation successful");
                }
            }
        }

        class DisplayData
        {
            string operation = String.Empty;
            object payload = null;
            bool error = false;

            public DisplayData(string operation, object payload, bool error)
            {
                this.operation = operation;
                this.payload = payload;
                this.error = error;
            }

            public string Operation
            {
                get { return this.operation; }
                set { this.operation = value; }
            }


            public object Payload
            {
                get { return this.payload; }
                set { this.payload = value; }
            }

            public bool Error
            {
                get { return this.error; }
                set { this.error = value; }
            }


        }
    }
}
